﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security;
using System.Windows;
using System.Windows.Browser;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace YouVisio.Silverlight.TextDiff
{
    public class TextDiffViewer : ContentControl, ITemplateSource, ISettingsSource
    {
        public const string Name_RootGrid = "RootGrid";
        public const string Name_TriggerButton = "TriggerButton";
        public const string Name_LeftInput = "LeftInput";
        public const string Name_RightInput = "RightInput";
        public const string Name_LeftOutputLineNumbers = "LeftOutputLineNumbers";
        public const string Name_LeftOutput = "LeftOutput";
        public const string Name_RightOutputLineNumbers = "RightOutputLineNumbers";
        public const string Name_RightOutput = "RightOutput";
        public const string Name_HorizontalScrollBar = "HorizontalScrollBar";
        public const string Name_OutputOuterGrid = "OutputOuterGrid";
        public const string Name_RectangleCanvas = "RectangleCanvas";
        public const string Name_OutputScrollViewer = "OutputScrollViewer";
        public const string Name_LinesPreviewCanvas = "LinesPreviewCanvas";
        public const string Name_OutputInnerGrid = "OutputInnerGrid";
        public const string Name_File1Button = "File1Button";
        public const string Name_File1Label = "File1Label";
        public const string Name_File2Button = "File2Button";
        public const string Name_File2Label = "File2Label";
        public const string Name_MessageLabel = "MessageLabel";
        public const string Name_WaitCursor = "WaitCursor";
        

        private readonly ITextDiffFactory _factory;
        private ITextDiffViewerUI _uiElements;

        static internal TextDiffViewer instance;

        public TextDiffViewer()
        {
            instance = this;

            this.DefaultStyleKey = typeof(TextDiffViewer);

            _factory = TextDiffFactory.Create(FactoryType);
            _factory.Initialize(this, this);

        }



        public static readonly DependencyProperty FactoryTypeProperty = 
            DependencyProperty.Register("FactoryType", typeof(string), typeof(TextDiffViewer), new PropertyMetadata(null));
        public string FactoryType
        {
            get { return (string)GetValue(FactoryTypeProperty); }
            set { SetValue(FactoryTypeProperty, value); }
        }
        #region ISettingsSource
        public static readonly DependencyProperty IsVisibleLinesPreviewBarProperty = ToDependencyProperty("IsVisibleLinesPreviewBar", true);
        public bool IsVisibleLinesPreviewBar
        {
            get { return (bool)GetValue(IsVisibleLinesPreviewBarProperty); }
            set { SetValue(IsVisibleLinesPreviewBarProperty, value); }
        }
        public static readonly DependencyProperty IsVisibleLeftLinesNumbersBarProperty = ToDependencyProperty("IsVisibleLeftLinesNumbersBar", true);
        public bool IsVisibleLeftLinesNumbersBar
        {
            get { return (bool)GetValue(IsVisibleLeftLinesNumbersBarProperty); }
            set { SetValue(IsVisibleLeftLinesNumbersBarProperty, value); }
        }
        public static readonly DependencyProperty IsVisibleRightLinesNumbersBarProperty = ToDependencyProperty("IsVisibleRightLinesNumbersBar", true);
        public bool IsVisibleRightLinesNumbersBar
        {
            get { return (bool)GetValue(IsVisibleRightLinesNumbersBarProperty); }
            set { SetValue(IsVisibleRightLinesNumbersBarProperty, value); }
        }
        public static readonly DependencyProperty IsVisibleButtonsBarProperty = ToDependencyProperty("IsVisibleButtonsBar", true);
        public bool IsVisibleButtonsBar
        {
            get { return (bool)GetValue(IsVisibleButtonsBarProperty); }
            set { SetValue(IsVisibleButtonsBarProperty, value); }
        }
        public static readonly DependencyProperty IsEnableFileSelectProperty = ToDependencyProperty("IsEnableFileSelect", true);
        public bool IsEnableFileSelect
        {
            get { return (bool)GetValue(IsEnableFileSelectProperty); }
            set { SetValue(IsEnableFileSelectProperty, value); }
        }
        public static readonly DependencyProperty IsEnableFileDragAndDropProperty = ToDependencyProperty("IsEnableFileDragAndDrop", true);
        public bool IsEnableFileDragAndDrop
        {
            get { return (bool)GetValue(IsEnableFileDragAndDropProperty); }
            set { SetValue(IsEnableFileDragAndDropProperty, value); }
        }
        public static readonly DependencyProperty IsEnableTextValidationProperty = ToDependencyProperty("IsEnableTextValidation", true);
        public bool IsEnableTextValidation
        {
            get { return (bool)GetValue(IsEnableTextValidationProperty); }
            set { SetValue(IsEnableTextValidationProperty, value); }
        }
        public static readonly DependencyProperty ElementSpacingProperty = 
            ToDependencyProperty("ElementSpacing", 2.0);
        public double ElementSpacing
        {
            get { return (double)GetValue(ElementSpacingProperty); }
            set { SetValue(ElementSpacingProperty, value); }
        }
        public static readonly DependencyProperty BlockDeletedBrushProperty =
            ToDependencyProperty("BlockDeletedBrush", new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xCA, 0xCB)));
        public Brush BlockDeletedBrush
        {
            get { return (Brush)GetValue(BlockDeletedBrushProperty); }
            set { SetValue(BlockDeletedBrushProperty, value); }
        }
        public static readonly DependencyProperty BlockInserterdBrushProperty = 
            ToDependencyProperty("BlockInsertedBrush", new SolidColorBrush(Color.FromArgb(0xFF, 0xAF, 0xEE, 0xEE)));
        public Brush BlockInsertedBrush
        {
            get { return (Brush)GetValue(BlockInserterdBrushProperty); }
            set { SetValue(BlockInserterdBrushProperty, value); }
        }
        public static readonly DependencyProperty BlockChangedBrushProperty =
            ToDependencyProperty("BlockChangedBrush", new SolidColorBrush(Color.FromArgb(0xFF, 0x99, 0xFB, 0x99)));
        public Brush BlockChangedBrush
        {
            get { return (Brush)GetValue(BlockChangedBrushProperty); }
            set { SetValue(BlockChangedBrushProperty, value); }
        }
        public static readonly DependencyProperty PreviewWindowFillBrushProperty =
            ToDependencyProperty("PreviewWindowFillBrush", new SolidColorBrush(Color.FromArgb(0x55, 0x33, 0x99, 0xFF)));
        public Brush PreviewWindowFillBrush
        {
            get { return (Brush)GetValue(PreviewWindowFillBrushProperty); }
            set { SetValue(PreviewWindowFillBrushProperty, value); }
        }
        public static readonly DependencyProperty PreviewWindowStrokeBrushProperty =
            ToDependencyProperty("PreviewWindowStrokeBrush", new SolidColorBrush(Color.FromArgb(0xFF, 0x33, 0x99, 0xFF)));
        public Brush PreviewWindowStrokeBrush
        {
            get { return (Brush)GetValue(PreviewWindowStrokeBrushProperty); }
            set { SetValue(PreviewWindowStrokeBrushProperty, value); }
        }
        public static readonly DependencyProperty PreviewWindowSideMarginProperty = ToDependencyProperty("PreviewWindowSideMargin", 7.0);
        public double PreviewWindowSideMargin
        {
            get { return (double)GetValue(PreviewWindowSideMarginProperty); }
            set { SetValue(PreviewWindowSideMarginProperty, value); }
        }
        public static readonly DependencyProperty MaxAllowedStringSizeProperty = ToDependencyProperty("MaxAllowedStringSize", 700000);
        public int MaxAllowedStringSize
        {
            get { return (int)GetValue(MaxAllowedStringSizeProperty); }
            set { SetValue(MaxAllowedStringSizeProperty, value); }
        }
        public static readonly DependencyProperty MaxAllowedFileSizeInBytesProperty = ToDependencyProperty("MaxAllowedFileSizeInBytes", 1024 * 1024);
        public int MaxAllowedFileSizeInBytes
        {
            get { return (int)GetValue(MaxAllowedFileSizeInBytesProperty); }
            set { SetValue(MaxAllowedFileSizeInBytesProperty, value); }
        }
        private readonly HashSet<string> _allowedFileExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
        public static readonly DependencyProperty AllowedFileTypesProperty = ToDependencyProperty("AllowedFileTypes", "*.txt;*.js;*.html;*.htm;*.bat;*.reg;*.xml;*.xaml;*.as;*.php;*.php4;*.cpp;*.c;*.java;*.pl;*.pt;*.fs;*.cs;*.vb;*.ini;*.aspx;*.ascx;*.asmx;*.asax;*.dae");
        public string AllowedFileTypes
        {
            get { return (string)GetValue(AllowedFileTypesProperty); }
            set { SetValue(AllowedFileTypesProperty, value); }
        }
        public bool IsAllowedFileName(string fileName)
        {
            EnsureAllowedFileExtensions();
            if (this._allowedFileExtensions.Count == 0) return true;
            var ext = Path.GetExtension(fileName);
            return this._allowedFileExtensions.Contains(ext);
        }
        public static readonly DependencyProperty Label_File1Property = ToDependencyProperty("Label_File1", "File 1");
        public string Label_File1
        {
            get { return (string)GetValue(Label_File1Property); }
            set { SetValue(Label_File1Property, value); }
        }
        public static readonly DependencyProperty Label_File2Property = ToDependencyProperty("Label_File2", "File 2");
        public string Label_File2
        {
            get { return (string)GetValue(Label_File2Property); }
            set { SetValue(Label_File2Property, value); }
        }
        public static readonly DependencyProperty Label_TextOrSourceFilesProperty = ToDependencyProperty("Label_TextOrSourceFiles", "Text or Source Files");
        public string Label_TextOrSourceFiles
        {
            get { return (string)GetValue(Label_TextOrSourceFilesProperty); }
            set { SetValue(Label_TextOrSourceFilesProperty, value); }
        }
        public static readonly DependencyProperty Label_Error_FilesWithThatExtensionNotAllowedProperty = 
            ToDependencyProperty("Label_Error_FilesWithThatExtensionNotAllowed", "Files with extension '{0}' are not allowed, please use only text or source files");
        public string Label_Error_FilesWithThatExtensionNotAllowed
        {
            get { return (string)GetValue(Label_Error_FilesWithThatExtensionNotAllowedProperty); }
            set { SetValue(Label_Error_FilesWithThatExtensionNotAllowedProperty, value); }
        }
        public static readonly DependencyProperty Label_Error_FilesWithThatSizeNotAllowedProperty = 
            ToDependencyProperty("Label_Error_FilesWithThatSizeNotAllowed", "Maximum file size allowed is {0} KB");
        public string Label_Error_FilesWithThatSizeNotAllowed
        {
            get { return (string)GetValue(Label_Error_FilesWithThatSizeNotAllowedProperty); }
            set { SetValue(Label_Error_FilesWithThatSizeNotAllowedProperty, value); }
        }
        public static readonly DependencyProperty Label_Error_StringsWithThatSizeNotAllowedProperty =
            ToDependencyProperty("Label_Error_StringsWithThatSizeNotAllowed", "Maximum string size allowed is {0} characters");
        public string Label_Error_StringsWithThatSizeNotAllowed
        {
            get { return (string)GetValue(Label_Error_StringsWithThatSizeNotAllowedProperty); }
            set { SetValue(Label_Error_StringsWithThatSizeNotAllowedProperty, value); }
        }
        
        public static readonly DependencyProperty Label_Error_PleaseEnterTextProperty = 
            ToDependencyProperty("Label_Error_PleaseEnterText", "Please enter text on both sides");
        public string Label_Error_PleaseEnterText
        {
            get { return (string)GetValue(Label_Error_PleaseEnterTextProperty); }
            set { SetValue(Label_Error_PleaseEnterTextProperty, value); }
        }
        public static readonly DependencyProperty Label_TextIsIdenticalProperty =
            ToDependencyProperty("Label_TextIsIdentical", "The text is identical");
        public string Label_TextIsIdentical
        {
            get { return (string)GetValue(Label_TextIsIdenticalProperty); }
            set { SetValue(Label_TextIsIdenticalProperty, value); }
        }



        public static readonly DependencyProperty Label_DeletedProperty = ToDependencyProperty("Label_Deleted", "Deleted");
        public string Label_Deleted
        {
            get { return (string)GetValue(Label_DeletedProperty); }
            set { SetValue(Label_DeletedProperty, value); }
        }
        public static readonly DependencyProperty Label_InsertedProperty = ToDependencyProperty("Label_Inserted", "Inserted");
        public string Label_Inserted
        {
            get { return (string)GetValue(Label_InsertedProperty); }
            set { SetValue(Label_InsertedProperty, value); }
        }
        public static readonly DependencyProperty Label_ChangedProperty = ToDependencyProperty("Label_Changed", "Changed");
        public string Label_Changed
        {
            get { return (string)GetValue(Label_ChangedProperty); }
            set { SetValue(Label_ChangedProperty, value); }
        }
        public static readonly DependencyProperty Label_VisibleWindowProperty = ToDependencyProperty("Label_VisibleWindow", "Visible Window");
        public string Label_VisibleWindow
        {
            get { return (string)GetValue(Label_VisibleWindowProperty); }
            set { SetValue(Label_VisibleWindowProperty, value); }
        }
        public static readonly DependencyProperty Label_ViewDifferencesProperty = ToDependencyProperty("Label_ViewDifferences", "View Differences!");
        public string Label_ViewDifferences
        {
            get { return (string)GetValue(Label_ViewDifferencesProperty); }
            set { SetValue(Label_ViewDifferencesProperty, value); }
        }
        public static readonly DependencyProperty Label_EditTextProperty = ToDependencyProperty("Label_EditText", "Edit Text");
        public string Label_EditText
        {
            get { return (string)GetValue(Label_EditTextProperty); }
            set { SetValue(Label_EditTextProperty, value); }
        }

        #endregion

        private void EnsureAllowedFileExtensions()
        {
            if (this._allowedFileExtensions.Count == 0 && !String.IsNullOrEmpty(AllowedFileTypes))
            {
                AllowedFileTypes.Split(';')
                    .Aggregate(this._allowedFileExtensions,
                                (hs, s) =>
                                {
                                    hs.Add(s.StartsWith("*.") ? s.Substring(1) : s);
                                    return hs;
                                });
            }
        }
        private static DependencyProperty ToDependencyProperty<T>(string name, T defaultValue)
        {
            return DependencyProperty.Register(name, typeof(T), typeof(TextDiffViewer), new PropertyMetadata(defaultValue));
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();


            _factory.TextDiffViewerUI.ParseTemplate(this);
            _uiElements = _factory.TextDiffViewerUI;
            _uiElements.OnApplyTemplate();


        }

        protected override Size MeasureOverride(Size availableSize)
        {
            availableSize = base.MeasureOverride(availableSize);
            return _uiElements.MeasureOverride(availableSize);
        }
        protected override Size ArrangeOverride(Size finalSize)
        {
            finalSize = base.ArrangeOverride(finalSize);
            return _uiElements.ArrangeOverride(finalSize);
        }

        public void FindTextDifference()
        {
            _uiElements.SetScreen(Screen.Preview);
            
        }
        public void SetTexts(string initial, string changed)
        {
            _uiElements.SetScreen(Screen.Edit);

            _uiElements.SetLeftText(initial);
            _uiElements.SetRightText(changed);
        }
        public T GetTemplateChild<T>(string name) where T : DependencyObject
        {
            return GetTemplateChild(name) as T;
        }
    }
}

